package cn.iocoder.yudao.module.bpm.service.message;

import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.module.bpm.convert.message.BpmMessageConvert;
import cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import com.alibaba.fastjson.JSON;
import com.aliyun.dingtalktodo_1_0.models.*;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.taobao.api.ApiException;
import com.xingyuv.jushauth.config.AuthConfig;
import com.xingyuv.jushauth.exception.AuthException;
import com.xingyuv.jushauth.request.AuthRequest;
import com.xingyuv.justauth.AuthRequestFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * BPM 消息 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class BpmMessageServiceImpl implements BpmMessageService {

    @Resource
    private SmsSendApi smsSendApi;

    @Resource
    private WebProperties webProperties;
    @Resource
    private AuthRequestFactory authRequestFactory;
    @Resource
    private SocialUserService socialUserService;

    @Override
    public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) {
        log.info("BpmMessageServiceImpl.sendMessageWhenProcessInstanceApprove：{}",JSON.toJSONString(reqDTO));
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(),
                BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsTemplateCode(), templateParams));
    }

    @Override
    public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) {
        log.info("BpmMessageServiceImpl.sendMessageWhenProcessInstanceReject：{}",JSON.toJSONString(reqDTO));
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
        templateParams.put("reason", reqDTO.getReason());
        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(),
                BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams));

        //删除待办（退回未捕捉到）
    }

    @Override
    public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) {
        log.info("BpmMessageServiceImpl.sendMessageWhenTaskAssigned：{}",JSON.toJSONString(reqDTO));
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
        templateParams.put("taskName", reqDTO.getTaskName());
        templateParams.put("startUserNickname", reqDTO.getStartUserNickname());
        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),
                BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams));

        //创建待办
        try {
            createTodo(getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())
                    , getOpenid(reqDTO.getAssigneeUserId())
                    , reqDTO.getTaskId()
                    , String.format("%s提交的%s，当前节点：%s", reqDTO.getStartUserNickname()
                            , reqDTO.getProcessInstanceName(), reqDTO.getTaskName()));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void sendMessageWhenTaskCompleted(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) {
        log.info("BpmMessageServiceImpl.sendMessageWhenTaskCompleted：{}",JSON.toJSONString(reqDTO));
        //完成待办
        try {
            updateTodo(getOpenid(reqDTO.getAssigneeUserId())
                    , reqDTO.getTaskId());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void sendMessageWhenTaskTimeout(BpmMessageSendWhenTaskTimeoutReqDTO reqDTO) {
        log.info("BpmMessageServiceImpl.sendMessageWhenTaskTimeout：{}",JSON.toJSONString(reqDTO));
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
        templateParams.put("taskName", reqDTO.getTaskName());
        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
        smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),
                BpmMessageEnum.TASK_TIMEOUT.getSmsTemplateCode(), templateParams));
    }

    private String getProcessInstanceDetailUrl(String taskId) {
        return webProperties.getAdminUi().getUrl() + "/bpm/process-instance/detail?id=" + taskId;
    }

    /**
     * 使用 Token 初始化账号Client
     * @return Client
     * @throws Exception
     */
    public static com.aliyun.dingtalktodo_1_0.Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalktodo_1_0.Client(config);
    }

    //获取token
    public String getToken(){
        AuthRequest authRequest = authRequestFactory.get("DINGTALK");
        AuthConfig authConfig=(AuthConfig) ReflectUtil.getFieldValue(authRequest, "config");
        //获取token
        DefaultDingTalkClient client0 = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
        OapiGettokenRequest request = new OapiGettokenRequest();
        request.setAppkey(authConfig.getClientId());
        request.setAppsecret(authConfig.getClientSecret());
        request.setHttpMethod("GET");

        OapiGettokenResponse rsp0 = null;
        log.info("请求request：{}", JSON.toJSONString(request));
        try {
            rsp0 = client0.execute(request);
        } catch (ApiException e) {
            // log.error("getAccessToken failed", e);
            System.out.println(e.toString());
            throw new AuthException(e.getErrMsg());
        }
        log.info("请求返回rsp0：{}", JSON.toJSONString(rsp0));
        String accessToken = rsp0.getAccessToken();
        return accessToken;
    }

    //创建待办
    public void createTodo(String url,String openid,String taskid,String content) throws Exception {
        com.aliyun.dingtalktodo_1_0.Client client = createClient();
        CreateTodoTaskHeaders createTodoTaskHeaders = new CreateTodoTaskHeaders();
        createTodoTaskHeaders.xAcsDingtalkAccessToken = getToken();

        CreateTodoTaskRequest.CreateTodoTaskRequestNotifyConfigs notifyConfigs = new CreateTodoTaskRequest.CreateTodoTaskRequestNotifyConfigs()
                .setDingNotify("1");
        CreateTodoTaskRequest.CreateTodoTaskRequestContentFieldList contentFieldList0 = new CreateTodoTaskRequest.CreateTodoTaskRequestContentFieldList();
        CreateTodoTaskRequest.CreateTodoTaskRequestDetailUrl detailUrl = new CreateTodoTaskRequest.CreateTodoTaskRequestDetailUrl()
                .setAppUrl(url)
                .setPcUrl(url);
        CreateTodoTaskRequest createTodoTaskRequest = new CreateTodoTaskRequest()
                .setOperatorId(openid)
                .setSourceId(taskid)
                .setSubject(content)
                .setCreatorId(openid)
                .setDescription("")
//                .setDueTime(1617675000000L)
                .setExecutorIds(java.util.Arrays.asList(openid))
                .setParticipantIds(java.util.Arrays.asList(openid))
                .setDetailUrl(detailUrl)
                .setContentFieldList(java.util.Arrays.asList(contentFieldList0))
                .setIsOnlyShowExecutor(true)
                .setPriority(20)
                .setNotifyConfigs(notifyConfigs);
        String errorCode = "0";
        String errorMsg = "";
        log.info("请求createTodoTaskRequest：{}，createTodoTaskHeaders：{}"
                , JSON.toJSONString(createTodoTaskRequest)
                ,JSON.toJSONString(createTodoTaskHeaders));
        CreateTodoTaskResponse res1 = null;
        try {
            res1 = client.createTodoTaskWithOptions(openid, createTodoTaskRequest, createTodoTaskHeaders, new RuntimeOptions());
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("错误返回1：{}", JSON.toJSONString(err));
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("错误返回2：{}", JSON.toJSONString(err));
            }

        }
        log.info("请求返回rsp1：{}", JSON.toJSONString(res1));
    }

    //更新钉钉待办-废弃
    public void updateTodobak(String openid,String taskid) throws Exception {
        //查询代办
        String token = getToken();
        com.aliyun.dingtalktodo_1_0.Client client = createClient();
        QueryOrgTodoTasksHeaders queryOrgTodoTasksHeaders = new QueryOrgTodoTasksHeaders();
        queryOrgTodoTasksHeaders.xAcsDingtalkAccessToken = token;
        QueryOrgTodoTasksRequest queryOrgTodoTasksRequest = new QueryOrgTodoTasksRequest()
                .setIsDone(false);
        try {
            QueryOrgTodoTasksResponse resp = client.queryOrgTodoTasksWithOptions(openid, queryOrgTodoTasksRequest, queryOrgTodoTasksHeaders, new RuntimeOptions());
            log.info("请求返回rsp1：{}", JSON.toJSONString(resp));
            List<QueryOrgTodoTasksResponseBody.QueryOrgTodoTasksResponseBodyTodoCards> list = resp.getBody().getTodoCards();
            for (int i = 0; i < list.size(); i++) {
                if(list.get(i).sourceId.equals(taskid)){
                    //更新待办
//                    com.aliyun.dingtalktodo_1_0.Client client = createClient();
                    UpdateTodoTaskHeaders updateTodoTaskHeaders = new UpdateTodoTaskHeaders();
                    updateTodoTaskHeaders.xAcsDingtalkAccessToken = token;
                    UpdateTodoTaskRequest updateTodoTaskRequest = new UpdateTodoTaskRequest()
                            .setDone(true);
                    client.updateTodoTaskWithOptions(openid, list.get(i).getTaskId(), updateTodoTaskRequest, updateTodoTaskHeaders, new RuntimeOptions());
                }
            }
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("错误返回1：{}", JSON.toJSONString(err));
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error("错误返回2：{}", JSON.toJSONString(err));
            }

        }
    }

    //更新钉钉待办
    public void updateTodo(String openid, String taskid) throws Exception {
        String token = getToken();
        com.aliyun.dingtalktodo_1_0.Client client = createClient();
        String nextToken = null; // 初始分页标记
        boolean found = false;

        do {
            try {
                // 1. 构建查询请求
                QueryOrgTodoTasksHeaders headers = new QueryOrgTodoTasksHeaders();
                headers.xAcsDingtalkAccessToken = token;

                QueryOrgTodoTasksRequest request = new QueryOrgTodoTasksRequest()
                        .setIsDone(false)
                        .setNextToken(nextToken); // 设置分页标记

                // 2. 执行查询
                QueryOrgTodoTasksResponse resp = client.queryOrgTodoTasksWithOptions(
                        openid, request, headers, new RuntimeOptions());

                log.debug("待办查询响应: {}", JSON.toJSONString(resp));

                // 3. 处理当前页数据
                List<QueryOrgTodoTasksResponseBody.QueryOrgTodoTasksResponseBodyTodoCards> list =
                        resp.getBody().getTodoCards();

                for (QueryOrgTodoTasksResponseBody.QueryOrgTodoTasksResponseBodyTodoCards item : list) {
                    if (taskid.equals(item.getSourceId())) {
                        // 4. 找到目标待办，执行更新
                        updateTodoTask(client, openid, item.getTaskId(), token);
                        found = true;
                        break;
                    }
                }

                // 5. 获取下一页标记
                nextToken = resp.getBody().getNextToken();

            } catch (TeaException err) {
                handleDingTalkError(err);
                break;
            } catch (Exception ex) {
                handleDingTalkError(new TeaException(ex.getMessage(), ex));
                break;
            }
        } while (!found && !StringUtils.isEmpty(nextToken)); // 未找到且还有下一页时继续

        if (!found) {
            log.warn("未找到匹配的待办任务，taskid: {}", taskid);
        }
    }

    // 更新待办任务状态
    private void updateTodoTask(com.aliyun.dingtalktodo_1_0.Client client, String openid, String taskId, String token) throws Exception {
        UpdateTodoTaskHeaders headers = new UpdateTodoTaskHeaders();
        headers.xAcsDingtalkAccessToken = token;

        UpdateTodoTaskRequest request = new UpdateTodoTaskRequest()
                .setDone(true);

        client.updateTodoTaskWithOptions(openid, taskId, request, headers, new RuntimeOptions());
        log.info("待办任务已标记完成，taskId: {}", taskId);
    }

    // 统一错误处理
    private void handleDingTalkError(TeaException err) {
        if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
            log.error("钉钉接口错误 - code: {}, message: {}", err.code, err.message);
        }
        throw new RuntimeException("钉钉待办操作失败", err);
    }

    /**
     * 获取钉钉用户的 openid
     * @param userid 系统用户 ID
     * @return 钉钉 openid
     * @throws IllegalArgumentException 当未绑定钉钉账号时抛出异常
     */
    public String getOpenid(Long userid) {
        // 1. 查询社交用户绑定关系
        SocialUserRespDTO socialUserRespDTO = socialUserService.getSocialUserByUserId(
                UserTypeEnum.ADMIN.getValue(),
                userid,
                SocialTypeEnum.DINGTALK_NOLOGIN.getType()
        );

        // 2. 验证查询结果
        if (socialUserRespDTO == null) {
            throw new IllegalArgumentException("用户未绑定钉钉账号，用户 ID: " + userid);
        }

        if (StringUtils.isEmpty(socialUserRespDTO.getOpenid())) {
            throw new IllegalArgumentException("钉钉账号未返回有效 openid，用户 ID: " + userid);
        }

        // 3. 返回 openid
        return socialUserRespDTO.getOpenid();
    }

    public void main(String[] args_) throws Exception {
        java.util.List<String> args = java.util.Arrays.asList(args_);

    }
}
